using Engine;
using Game;
using GameEntitySystem;
using TemplatesDatabase;

namespace SCIENEW {
    public class ComponentCokeOven : ComponentInventoryBase, ICrafterComponent, IUpdateable {
        public int RemainsSlotIndex => SlotsCount - 2;
        public int ResultSlotIndex => SlotsCount - 1;
        public int CatSlotIndex => SlotsCount - 3;

        public static int RequiredHeatLevel => -7;

        public UpdateOrder UpdateOrder => UpdateOrder.Default;

        public bool IsIntegrity { get; private set; } = false;

        public ComponentBlockEntity m_componentBlockEntity;
        public SubsystemTerrain m_subsystemTerrain;
        public SubsystemTime m_subsystemTime;
        public SubsystemExplosions m_subsystemExplosions;

        public ComponentFireBox m_componentFireBox;//燃烧室
        public ComponentAirBlower m_componentAirBlower;

        CraftingRecipe m_craftingRecipe = null!;
        bool m_updateCraftingRecipe = true;
        public float m_craftingProgress = 0f;
        float m_speed = 1 / 8f;

        public bool IsWorking => IsIntegrity && m_componentFireBox != null && m_componentAirBlower != null && m_componentFireBox.IsPreheating() && m_componentAirBlower.IsWorking();

        public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap) {
            base.Load(valuesDictionary, idToEntityMap);
            m_componentBlockEntity = Entity.FindComponent<ComponentBlockEntity>(true);
            m_subsystemTerrain = Project.FindSubsystem<SubsystemTerrain>(true);
            m_subsystemTime = Project.FindSubsystem<SubsystemTime>(true);
            m_subsystemExplosions = Project.FindSubsystem<SubsystemExplosions>(true);
            m_updateCraftingRecipe = true;
        }

        public override int GetSlotCapacity(int slotIndex, int value) {
            if (slotIndex < CatSlotIndex) {
                if (m_slots[0..CatSlotIndex].All(x => x.Count == 0)) return base.GetSlotCapacity(slotIndex, value); //原料格子全空

                int blockValue = m_slots[0..CatSlotIndex].First(x => x.Count > 0).Value;//原料格子存在一个没有空的，强制单一种类物品
                if (value == blockValue) return base.GetSlotCapacity(slotIndex, value);
                return 0;
            }
            if (slotIndex == ResultSlotIndex || slotIndex == RemainsSlotIndex) {
                return 0;
            }
            if (slotIndex == CatSlotIndex) return 0;
            return base.GetSlotCapacity(slotIndex, value);
        }

        public override void AddSlotItems(int slotIndex, int value, int count) {
            m_updateCraftingRecipe = true;
            base.AddSlotItems(slotIndex, value, count);
        }

        public override int RemoveSlotItems(int slotIndex, int count) {
            m_updateCraftingRecipe = true;
            return base.RemoveSlotItems(slotIndex, count);
        }

        public void Update(float dt) {
            TerrainChunk chunkAtCell = m_subsystemTerrain.Terrain.GetChunkAtCell(m_componentBlockEntity.Coordinates.X, m_componentBlockEntity.Coordinates.Z);
            if (chunkAtCell != null
                && chunkAtCell.State is TerrainChunkState.Valid or TerrainChunkState.InvalidLight or TerrainChunkState.InvalidPropagatedLight) {
                if (m_subsystemTime.PeriodicGameTimeEvent(0.2f, 0.0f)) {//一秒5次检测
                    CheckIntegrity();
                }
            }

            UpdateSmelting(dt);
            ReplaceFurnace(m_componentBlockEntity.Coordinates);
        }

        void UpdateSmelting(float dt) {
            if (IsWorking) {
                if (m_updateCraftingRecipe) {
                    m_updateCraftingRecipe = false;
                    FindRecipe();
                }
                if (m_craftingRecipe == null) {
                    m_craftingProgress = 0f;
                }
                else {//进度增加，最后合成物品
                    m_craftingProgress = MathUtils.Min(m_craftingProgress + m_speed * dt, 1f);
                    if (m_craftingProgress >= 1f) {
                        //移除物品
                        Slot slot = m_slots.FirstOrDefault(x => x.Count >= m_craftingRecipe.Ingredients.Length, null);
                        if (slot != null) {
                            slot.Count -= m_craftingRecipe.Ingredients.Length;
                        }
                        m_slots[ResultSlotIndex].Value = m_craftingRecipe.ResultValue;
                        m_slots[ResultSlotIndex].Count += m_craftingRecipe.ResultCount;
                        m_craftingRecipe = null;
                        m_craftingProgress = 0f;
                        m_updateCraftingRecipe = true;
                    }
                }
            }
            else {
                m_craftingProgress = 0f;
                m_craftingRecipe = null;
                m_updateCraftingRecipe = true;
            }
        }

        void CheckIntegrity() {
            m_componentFireBox = null;
            m_componentAirBlower = null;

            Point3 corePos = m_componentBlockEntity.Coordinates;
            int coreValue = m_subsystemTerrain.Terrain.GetCellValueFast(corePos.X, corePos.Y, corePos.Z);
            int face = IlluminableDevice.GetDirection_Static(coreValue);//核心朝向
            Point3 centerPos = corePos - CellFace.FaceToPoint3(face);//结构中心的坐标

            //第一层
            for (int x = -1; x <= 1; x++) {
                for (int z = -1; z <= 1; z++) {//检测3*3耐火砖
                    Point3 testPos = centerPos + new Point3(x, -1, z);
                    if (CheckFireBrick(testPos.X, testPos.Y, testPos.Z)) { continue; }//检测到耐火砖，继续下一块
                    //检测到非法方块，不成形
                    IsIntegrity = false;
                    return;
                }
            }
            int[] sideFaces = new int[2];
            int fireBoxSideFace = sideFaces[0];//有燃烧室的那一面
            DevicesUtils.GetSideFaceOfXZ(face, out sideFaces[0], out sideFaces[1]);
            foreach (var sideFace in sideFaces) {//检测燃烧室
                Point3 testPos = centerPos - Point3.UnitY + CellFace.FaceToPoint3(sideFace) * 2;
                if (DeviceEntityUtils.GetDeviceEntity(m_subsystemTerrain, testPos, out ComponentBlockEntity fireBoxEntity)) {//检测到了燃烧室，就停止检测
                    m_componentFireBox = fireBoxEntity.Entity.FindComponent<ComponentFireBox>();
                    if (m_componentFireBox != null) {
                        fireBoxSideFace = sideFace;
                        break;
                    }
                }
            }
            if (m_componentFireBox == null) {//两侧都没有检测到燃烧室，直接判断未成形
                IsIntegrity = false;
                return;
            }

            //第二层
            Point3 backPoint = centerPos - CellFace.FaceToPoint3(face);//核心背对的面
            Point3 rightPoint = centerPos - CellFace.FaceToPoint3(fireBoxSideFace);//鼓风机背对的面
            Point3 leftPoint = centerPos + CellFace.FaceToPoint3(fireBoxSideFace);//鼓风机正对的面
            bool center = m_subsystemTerrain.Terrain.GetCellContentsFast(centerPos.X, centerPos.Y, centerPos.Z) is AirBlock.Index;
            bool corner1 = CheckFireBrick(centerPos.X - 1, centerPos.Y, centerPos.Z - 1);//检测四个角
            bool corner2 = CheckFireBrick(centerPos.X - 1, centerPos.Y, centerPos.Z + 1);
            bool corner3 = CheckFireBrick(centerPos.X + 1, centerPos.Y, centerPos.Z + 1);
            bool corner4 = CheckFireBrick(centerPos.X + 1, centerPos.Y, centerPos.Z - 1);
            bool back = CheckFireBrick(backPoint.X, backPoint.Y, backPoint.Z);
            bool right = CheckFireBrick(rightPoint.X, rightPoint.Y, rightPoint.Z);
            bool left = m_subsystemTerrain.Terrain.GetCellContentsFast(leftPoint.X, leftPoint.Y, leftPoint.Z) is AirBlock.Index;
            if (!(center && corner1 && corner2 && corner3 && corner4 && back && right && left)) {//四个角不完全满足是砖块，未成形
                IsIntegrity = false;
                return;
            }
            Point3 airBlowerPoint = centerPos + CellFace.FaceToPoint3(fireBoxSideFace) * 2;//鼓风机位置
            if (DeviceEntityUtils.GetDeviceEntity(m_subsystemTerrain, airBlowerPoint, out ComponentBlockEntity airBlowerEntity)) {//没找到鼓风机，未成形
                m_componentAirBlower = airBlowerEntity.Entity.FindComponent<ComponentAirBlower>(false);
                if (m_componentAirBlower == null) {
                    IsIntegrity = false;
                    return;
                }
            }
            else {
                IsIntegrity = false;
                return;
            }

            //第三层
            for (int x = -1; x <= 1; x++) {
                for (int z = -1; z <= 1; z++) {//检测3*3耐火砖
                    Point3 testPos = centerPos + new Point3(x, 1, z);
                    if (CheckFireBrick(testPos.X, testPos.Y, testPos.Z)) { continue; }//检测到耐火砖，继续下一块
                    if (x == 0 && z == 0 && CheckPort(testPos.X, testPos.Y, testPos.Z)) { continue; }
                    //检测到非法方块，不成形
                    IsIntegrity = false;
                    return;
                }
            }

            //检测流程下来没有任何不符，已成型
            IsIntegrity = true;

            bool CheckFireBrick(int x, int y, int z) {
                int blockValue = m_subsystemTerrain.Terrain.GetCellValueFast(x, y, z);
                return Terrain.ExtractContents(blockValue) == BaseNormalBlock.Index && NormalBlockManager.GetNormalBlock(blockValue).GetCraftingId() == "FirebrickWall";
            }
            bool CheckPort(int x, int y, int z) {
                int blockValue = m_subsystemTerrain.Terrain.GetCellValueFast(x, y, z);
                return Terrain.ExtractContents(blockValue) == BaseDeviceBlock.Index && DevicesBlockManager.GetDevice(blockValue).GetCraftingId() == "CokeOvenPortDevice";
            }
        }

        void FindRecipe() {
            m_craftingRecipe = null;
            foreach (var slot in m_slots) {
                if (slot.Count == 0) continue;

                //寻找配方
                ComponentPlayer componentPlayer = FindInteractingPlayer();
                float playerLevel = componentPlayer?.PlayerData.Level ?? 1;
                string ingredient = IECraftingRecipesManager.BlockToIngredient(slot.Value);
                string[] ingredients = Enumerable.Repeat(ingredient, slot.Count).ToArray();

                m_craftingRecipe = MatchRecipe(m_subsystemTerrain, ingredients, RequiredHeatLevel, playerLevel);
                if (m_craftingRecipe == null) continue;

                //检查配方
                Slot resultSlot = m_slots[ResultSlotIndex];
                int resultID = Terrain.ExtractContents(m_craftingRecipe.ResultValue);
                if (resultSlot.Count != 0
                    && (m_craftingRecipe.ResultValue != resultSlot.Value || m_craftingRecipe.ResultCount + resultSlot.Count > BlocksManager.Blocks[resultID].GetMaxStacking(m_craftingRecipe.ResultValue))) {
                    m_craftingRecipe = null;
                }
                if (m_craftingRecipe == null) continue;

                Slot remainSlot = m_slots[RemainsSlotIndex];
                int remainID = Terrain.ExtractContents(m_craftingRecipe.RemainsValue);
                if (remainSlot.Count != 0
                    && (m_craftingRecipe.RemainsValue != remainSlot.Value || m_craftingRecipe.RemainsCount + remainSlot.Count > BlocksManager.Blocks[remainID].GetMaxStacking(m_craftingRecipe.RemainsValue))) {
                    m_craftingRecipe = null;
                }

                if (m_craftingRecipe != null) break;
            }
        }

        void ReplaceFurnace(Point3 coordinates) {
            TerrainChunk chunkAtCell = m_subsystemTerrain.Terrain.GetChunkAtCell(coordinates.X, coordinates.Z);
            if (chunkAtCell != null
                && chunkAtCell.State == TerrainChunkState.Valid) {
                int cellValue = m_subsystemTerrain.Terrain.GetCellValue(coordinates.X, coordinates.Y, coordinates.Z);
                int direction = IlluminableDevice.GetDirection_Static(cellValue);
                int deviceValue = DevicesBlockManager.GetDeviceValueByType(typeof(CokeOvenDevice), IsWorking ? 0b100 | direction : direction);
                m_subsystemTerrain.ChangeCell(coordinates.X, coordinates.Y, coordinates.Z, deviceValue);
            }
        }

        public CraftingRecipe MatchRecipe(SubsystemTerrain terrain, string[] ingredients, float heatLevel, float playerLevel) {
            foreach (var recipe in CraftingRecipesManager.Recipes) {
                if (recipe.RequiredHeatLevel != RequiredHeatLevel) continue;
                string recipeIngredient = recipe.Ingredients.First(x => !string.IsNullOrEmpty(x)); //配方中第一个 物品
                int recipeIngredientCount = recipe.Ingredients.Count(x => x == recipeIngredient); //配方中第一个物品在配方里的数量
                if (IECraftingRecipesManager.CompareIngredients(recipeIngredient, ingredients[0])
                    && ingredients.Length >= recipeIngredientCount) {//传入ingredients中第一个物品与配方第一个物品相同并且传入ingredients数量大于配方第一个物品所需数量，则找到了
                    CraftingRecipe newRecipe = new() {
                        Description = recipe.Description,
                        Ingredients = Enumerable.Repeat(recipeIngredient, recipeIngredientCount).ToArray(),
                        Message = recipe.Message,
                        RemainsCount = recipe.RemainsCount,
                        RemainsValue = recipe.RemainsValue,
                        ResultCount = recipe.ResultCount,
                        ResultValue = recipe.ResultValue,
                        RequiredHeatLevel = recipe.RequiredHeatLevel,
                        RequiredPlayerLevel = recipe.RequiredPlayerLevel
                    };
                    return newRecipe;
                }
            }
            return null;
        }
    }
}